use rust_mcp_sdk::macros::{mcp_tool, JsonSchema};
use rust_mcp_sdk::schema::{schema_utils::CallToolError, CallToolResult, TextContent};
use serde_json::json;

use crate::tms_client::TmsClient;

#[mcp_tool(
    name = "list_orders",
    title = "List Orders",
    description = "List all orders in the TMS system with pagination and status filtering. Returns order details including customer, pickup/delivery addresses, and current status.",
    destructive_hint = false,
    idempotent_hint = true,
    open_world_hint = false,
    read_only_hint = true
)]
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema)]
pub struct ListOrdersTool {
    /// Page number for pagination (default: 1)
    #[serde(default = "default_page")]
    pub page: i32,
    /// Number of items per page (default: 10, max: 100)
    #[serde(default = "default_page_size")]
    pub page_size: i32,
    /// Filter by order status (optional): 0=Pending, 1=Assigned, 2=PickedUp, 3=InTransit, 4=Delivered, 5=Cancelled
    pub status: Option<i32>,
}

fn default_page() -> i32 { 1 }
fn default_page_size() -> i32 { 10 }

impl ListOrdersTool {
    pub async fn run_tool(params: Self, client: &mut TmsClient) -> Result<CallToolResult, CallToolError> {
        match client.list_orders(params.page, params.page_size, params.status.unwrap_or(0)).await {
            Ok(orders) => {
                let response = json!({
                    "orders": format!("{:?}", orders),
                    "page": params.page,
                    "page_size": params.page_size,
                    "total": orders.total
                });
                Ok(CallToolResult::text_content(vec![TextContent::from(response.to_string())]))
            },
            Err(e) => Err(CallToolError::new(std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to list orders: {}", e)))),
        }
    }
}

#[mcp_tool(
    name = "create_order",
    title = "Create Order",
    description = "Create a new transportation order in the TMS system. Requires customer information, pickup/delivery addresses, cargo details, and time windows.",
    destructive_hint = true,
    idempotent_hint = false,
    open_world_hint = false,
    read_only_hint = false
)]
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema)]
pub struct CreateOrderTool {
    /// Customer ID
    pub customer_id: String,
    /// Pickup address
    pub pickup_address: String,
    /// Delivery address
    pub delivery_address: String,
    /// Cargo weight in kg
    pub weight: f64,
    /// Cargo volume in cubic meters
    pub volume: f64,
    /// Pickup time window start (Unix timestamp)
    pub pickup_time_window_start: i64,
    /// Pickup time window end (Unix timestamp)
    pub pickup_time_window_end: i64,
    /// Delivery time window start (Unix timestamp)
    pub delivery_time_window_start: i64,
    /// Delivery time window end (Unix timestamp)
    pub delivery_time_window_end: i64,
    /// Special requirements (optional)
    pub special_requirements: Option<String>,
}

impl CreateOrderTool {
    pub async fn run_tool(params: Self, client: &mut TmsClient) -> Result<CallToolResult, CallToolError> {
        match client.create_order(
            params.customer_id,
            params.pickup_address,
            params.delivery_address,
            params.weight,
            params.volume,
            params.pickup_time_window_start,
            params.pickup_time_window_end,
            params.delivery_time_window_start,
            params.delivery_time_window_end,
            params.special_requirements.unwrap_or_default(),
        ).await {
            Ok(order) => {
                let response = json!({
                    "message": "Order created successfully",
                    "order": format!("{:?}", order)
                });
                Ok(CallToolResult::text_content(vec![TextContent::from(response.to_string())]))
            },
            Err(e) => Err(CallToolError::new(std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to create order: {}", e)))),
        }
    }
}

#[mcp_tool(
    name = "get_order",
    title = "Get Order",
    description = "Get detailed information about a specific order by ID.",
    destructive_hint = false,
    idempotent_hint = true,
    open_world_hint = false,
    read_only_hint = true
)]
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema)]
pub struct GetOrderTool {
    /// Order ID
    pub id: String,
}

impl GetOrderTool {
    pub async fn run_tool(params: Self, client: &mut TmsClient) -> Result<CallToolResult, CallToolError> {
        match client.get_order(params.id).await {
            Ok(order) => {
                Ok(CallToolResult::text_content(vec![TextContent::from(format!("{:?}", order))]))
            },
            Err(e) => Err(CallToolError::new(std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to get order: {}", e)))),
        }
    }
}

#[mcp_tool(
    name = "update_order_status",
    title = "Update Order Status",
    description = "Update the status of an order. Status values: 0=Pending, 1=Assigned, 2=PickedUp, 3=InTransit, 4=Delivered, 5=Cancelled.",
    destructive_hint = false,
    idempotent_hint = false,
    open_world_hint = false,
    read_only_hint = false
)]
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema)]
pub struct UpdateOrderStatusTool {
    /// Order ID
    pub id: String,
    /// New status (0=Pending, 1=Assigned, 2=PickedUp, 3=InTransit, 4=Delivered, 5=Cancelled)
    pub status: i32,
    /// Optional notes about the status change
    pub notes: Option<String>,
}

impl UpdateOrderStatusTool {
    pub async fn run_tool(params: Self, client: &mut TmsClient) -> Result<CallToolResult, CallToolError> {
        match client.update_order_status(params.id, params.status, params.notes.unwrap_or_default()).await {
            Ok(order) => {
                let response = json!({
                    "message": "Order status updated successfully",
                    "order": format!("{:?}", order)
                });
                Ok(CallToolResult::text_content(vec![TextContent::from(response.to_string())]))
            },
            Err(e) => Err(CallToolError::new(std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to update order status: {}", e)))),
        }
    }
}